home *** CD-ROM | disk | FTP | other *** search
/ Aminet 3 / Aminet 3 - July 1994.iso / Aminet / misc / unix / tracker_4_3.lzh / tracker / Amiga / amiga_support.c next >
Encoding:
C/C++ Source or Header  |  1994-02-13  |  15.6 KB  |  771 lines

  1. /* support functions for yours truly amiga 
  2.     vi:se ts=3 sw=3:
  3.  */
  4.  
  5. /* $Id: amiga_support.c,v 1.4 1993/12/04 16:12:50 espie Exp espie $ */
  6. /* $Log: amiga_support.c,v $
  7.  * Revision 1.4  1993/12/04  16:12:50  espie
  8.  * BOOL -> boolean.
  9.  * Got rid of channel in resample.
  10.  *
  11.  * Revision 1.3  1993/12/02  15:45:33  espie
  12.  * Stupid fix + type casts.
  13.  *
  14.  * Revision 1.2  1993/11/17  15:29:53  espie
  15.  * Added higher-level support. Separated plainly from audio.c
  16.  * */
  17.  
  18. #include <stdio.h>
  19. #include "defs.h"
  20. #include "extern.h"
  21.  
  22. #include "song.h"
  23. #include "channel.h"
  24.  
  25. #include <hardware/cia.h>
  26. #include <hardware/intbits.h>
  27. #include <hardware/dmabits.h>
  28. #include <exec/nodes.h>
  29. #include <exec/memory.h>
  30. #include <dos/dos.h>
  31. #include <devices/audio.h>
  32. #include <devices/timer.h>
  33. #include <proto/exec.h>
  34. #include <proto/dos.h>
  35. #include <proto/timer.h>
  36. #undef X
  37. #include <proto/intuition.h>
  38. #include <proto/gadtools.h>
  39.  
  40. LOCAL struct audio_channel
  41.     {
  42.     int amiga_number;
  43.     struct sample_info *samp;
  44.     int volume;
  45.     int pitch;
  46.     } chan[4];
  47.  
  48. LOCAL struct sample_info dummy =
  49.     {
  50.     NULL,
  51.     0,
  52.     0,
  53.     0,
  54.     0,
  55.     0,
  56.     0,
  57.     0,
  58.     NULL,
  59.     NULL
  60.     };
  61.  
  62. LOCAL int allocated = 0;
  63.  
  64. struct audio_channel *new_channel()
  65.     {
  66.     struct audio_channel *new;
  67.  
  68.     new = &chan[allocated];
  69.     new->amiga_number = allocated++;
  70.     new->samp = &dummy;
  71.     new->volume = 0;
  72.     new->pitch = 0;
  73.     new->samp = 0;
  74.     return new;
  75.     }
  76.  
  77. void no_audio_channels()
  78.     {
  79.     allocated = 0;
  80.     }
  81.  
  82. /* list scanning.
  83.  * next is needed: with it, we can actually unlink the node while scanning
  84.  *    the current list.  Type is provided to avoid type-casting errors in
  85.  * SCANLIST expansion.
  86.  */
  87. #define SCANLIST(node, next, list, type) \
  88.     for((node) = (type)((struct MinList *)(list))->mlh_Head; \
  89.         (next) = (type)((struct MinNode *)(node))->mln_Succ; \
  90.         (node) = (next))
  91.  
  92. LOCAL char *version = "\0$VER: tracker 3.11";
  93.  
  94. /* remember allocated samples for cleaning up in panic case */
  95. LOCAL struct MinList tracked_list;
  96. /* timer variables:
  97.  *        the message port 
  98.  */
  99. LOCAL struct MsgPort *tport = 0;
  100. /*        the basic wait request */
  101. LOCAL struct timerequest *tr = 0;
  102. /*         the reference system time at which the song started */
  103. LOCAL struct EClockVal system_time;
  104.  
  105. LOCAL struct Library *TimerBase;
  106.  
  107. /* Clean up: Do we need to close timer ? */
  108. LOCAL boolean timer_opened = FALSE;
  109. /* Not really useful */
  110. LOCAL boolean first_time = FALSE;
  111. extern volatile struct CIA __far ciaa;
  112.  
  113. LOCAL void init_timer()
  114.     {
  115.     int fail;
  116.     tport = CreateMsgPort();
  117.     if (!tport)
  118.         {
  119.         exit(10);
  120.         }
  121.     tr = CreateIORequest(tport, sizeof(struct timerequest));
  122.     if (!tr)
  123.         {
  124.         exit(10);
  125.         }
  126.     fail = OpenDevice(TIMERNAME, UNIT_WAITECLOCK, (struct IORequest *)tr, 0);
  127.     if (fail)
  128.         exit(10);
  129.     else
  130.         timer_opened = TRUE;
  131.     TimerBase = tr->tr_node.io_Device;
  132.     }
  133.  
  134. /* The basic user interface (a simple window) */
  135. LOCAL struct IntuitionBase *IntuitionBase = 0;
  136. LOCAL struct Library *GadtoolsBase = 0;
  137. LOCAL struct Window *win;
  138. /* We use topaz.font so as not to get into sizing problems */
  139. LOCAL struct TextAttr topaz =
  140.     {
  141.     "topaz.font",
  142.     8,
  143.     0,
  144.     0
  145.     };
  146.  
  147. LOCAL struct NewGadget template =
  148.     {
  149.     10, 10,     /* start position (offset from left/topedge) */
  150.     72, 20,        /* width/height */
  151.     NULL,
  152.     &topaz,
  153.     0,            /* gadget ID */
  154.     0,
  155.     NULL,
  156.     NULL
  157.     };
  158.  
  159. LOCAL APTR vi;
  160. LOCAL struct Screen *pub = 0;
  161. LOCAL struct Gadget *glist;
  162.  
  163. /* we precisely have seven gadgets */
  164. #define MAX_GADGET 7
  165.  
  166. /* all labelled with strings */
  167. LOCAL char *label[MAX_GADGET] =
  168.     {
  169.     "Previous",
  170.     "Next",
  171.     "Restart",
  172.     "<<",
  173.     ">>", 
  174.     "NTSC",
  175.     "PAL",
  176.     };
  177.  
  178. /* and mapping to these commands of the normal tracker interface */
  179. LOCAL char mapto[MAX_GADGET] = { 'p', 'n', 'r', '<', '>', 'S', 's' };
  180.     
  181. LOCAL void init_ui(void)
  182.     {
  183.     struct Gadget *gad;
  184.     int i;
  185.  
  186.     IntuitionBase = OpenLibrary("intuition.library", 37);
  187.     if (!IntuitionBase)
  188.         exit(10);
  189.     GadtoolsBase = OpenLibrary("gadtools.library", 37);
  190.     if (!GadtoolsBase)
  191.         exit(10);
  192.     pub = LockPubScreen(NULL);
  193.     if (!pub)
  194.         exit(10);
  195.     vi = GetVisualInfo(pub, TAG_END);
  196.     if (!vi)
  197.         exit(10);
  198.     gad = CreateContext(&glist);
  199.     if (!gad)
  200.         exit(10);
  201.     template.ng_VisualInfo = vi;
  202.         /* set up Top/Left Edge of initial gadget according to Wbar */
  203.     template.ng_TopEdge += pub->WBorTop + pub->Font->ta_YSize + 1;
  204.     template.ng_LeftEdge += pub->WBorLeft;    /* which I forgot... */
  205.         /* lay out gadgets */
  206.     for (i = 0; i < MAX_GADGET; i++)
  207.         {
  208.         template.ng_GadgetText = label[i];
  209.         gad = CreateGadget(BUTTON_KIND, gad, &template, TAG_END);
  210.         if (!gad)
  211.             exit(10);
  212.         template.ng_LeftEdge += template.ng_Width + 5;
  213.         template.ng_GadgetID++;
  214.         }
  215.         
  216.     win = OpenWindowTags(NULL, 
  217.         WA_Title, "Experiment IV",
  218.         WA_Width, template.ng_LeftEdge + 10,    
  219.         WA_Height, template.ng_Height + template.ng_TopEdge + 10,
  220.         WA_MouseQueue, 35,    /* we can't always answer messages */
  221.         WA_DepthGadget, TRUE,
  222.         WA_CloseGadget, TRUE,
  223.         WA_DragBar, TRUE,
  224.         WA_SmartRefresh, TRUE,    /* don't want to be bothered with refresh */
  225.         WA_Gadgets, glist,
  226.         WA_IDCMP, IDCMP_CLOSEWINDOW | BUTTONIDCMP | IDCMP_REFRESHWINDOW,
  227.         TAG_DONE, 0);
  228.     if (!win)
  229.         exit(10);
  230.     GT_RefreshWindow(win, NULL);    
  231.     }
  232.  
  233.  
  234. /* the audio interface */
  235.  
  236. /* we allow for lots more audio requests than we need */
  237. #define QUEUE_LENGTH 50
  238.  
  239. LOCAL struct ext_audio 
  240.     {
  241.     struct IOAudio request;
  242.     struct ext_audio *next;
  243.     } 
  244. /* the audio queue */    
  245.     *first = 0, 
  246. /* the specific request that get used for closing/opening */
  247.     *req = 0;
  248.  
  249. LOCAL struct MsgPort *port = 0;
  250. /* in order to clean up afterwards: */
  251. LOCAL boolean audio_opened = FALSE;
  252.  
  253. LOCAL void command_audio(struct ext_audio *io);
  254. LOCAL void send_immediate(ULONG command, int mask);
  255.  
  256. LOCAL UBYTE whichchannel[] = {15};
  257.  
  258. /* allocate_channels(): sends a request to the audio.device for
  259.  * the channels.
  260.  */
  261. LOCAL void allocate_channels(void)
  262.    { 
  263.    struct ext_audio *sweep;
  264.  
  265.    req->request.ioa_Request.io_Command = ADCMD_ALLOCATE;
  266.    req->request.ioa_AllocKey = 0;
  267.    req->request.ioa_Data = whichchannel;
  268.    req->request.ioa_Length = sizeof(whichchannel);
  269.    command_audio(req);
  270.  
  271.    for (sweep = first; sweep; sweep = sweep->next)
  272.       sweep->request.ioa_AllocKey = req->request.ioa_AllocKey;
  273.    }
  274.     
  275. /* free_channels(): give back the channels.
  276.  */ 
  277. LOCAL void free_channels(void)
  278.    {
  279.    req->request.ioa_Request.io_Command = ADCMD_FREE;
  280.    command_audio(req);
  281.    }
  282.  
  283.  
  284. LOCAL struct ext_audio *create_request(void)
  285.    {
  286.    struct ext_audio *new;
  287.  
  288.    new = AllocMem(sizeof(struct ext_audio), MEMF_CLEAR|MEMF_PUBLIC);
  289.    if (req)
  290.       *new = *req;
  291.    return new;
  292.    }
  293.  
  294. /* creates a whole queue of audio requests */   
  295. LOCAL void create_queue(void)
  296.    {
  297.    struct ext_audio *last, *new;
  298.    int i;
  299.    
  300.    last = NULL;
  301.    for (i = 0; i < QUEUE_LENGTH; i++)
  302.       {
  303.       new = create_request();
  304.       new->next = last;
  305.       last = new;
  306.       }
  307.    first = new;
  308.    }
  309.  
  310. LOCAL void get_requests(void)
  311.    {
  312.    struct ext_audio *back;
  313.  
  314.    while(back = GetMsg(port))
  315.        if (back != req)        /* only those belonging to the queue */
  316.           {
  317.           back->next = first;
  318.           first = back;
  319.           }
  320.    }
  321.  
  322. LOCAL void send_immediate(ULONG command, int mask)
  323.    {
  324.    first->request.ioa_Request.io_Command = command;
  325.    first->request.ioa_Request.io_Flags = IOF_QUICK;
  326.    first->request.ioa_Request.io_Unit = mask;
  327.    BeginIO((struct IORequest *)first);
  328.    }
  329.  
  330. LOCAL void command_audio(struct ext_audio *io)
  331.    {
  332.    struct ext_audio *request;
  333.  
  334.    BeginIO((struct IORequest *)io);
  335.    WaitPort(port);
  336.     get_requests();
  337.    }
  338.  
  339. LOCAL void reset_audio(void)
  340.    {
  341.    send_immediate(CMD_RESET, 15);
  342.    get_requests();
  343.    }
  344.  
  345. /* obtain_audio(): allocate all the structures we will need to
  346.  * play with the audio device
  347.  */ 
  348. LOCAL void obtain_audio(void)
  349.    {
  350.    BYTE fail;
  351.    
  352.    port = CreateMsgPort();
  353.    if (!port)
  354.        {
  355.        fprintf(stderr, "Couldn't allocate port");
  356.         exit(10);
  357.         }
  358.    req = CreateIORequest(port, sizeof(struct ext_audio));
  359.    if (!req)
  360.        {
  361.        fprintf(stderr, "Couldn't allocate io request");
  362.        exit(10);
  363.         }
  364.    req->request.ioa_Request.io_Message.mn_Node.ln_Pri = 0;
  365.       /* Note that OpenDevice returns 0 on success
  366.        */
  367.    fail = OpenDevice("audio.device", 0L, (struct IORequest *)req, 0L);
  368.    if (fail)
  369.         {
  370.         fprintf(stderr, "Couldn't open audio device");
  371.         exit(10);
  372.         }
  373.     else
  374.         audio_opened = TRUE;
  375.    }
  376.  
  377. /* remember old task priority at exit (CLI process runs on the CLI context,
  378.  * and reverts to being a shell with the same priority on exit
  379.  */
  380. LOCAL int oldpri;
  381.  
  382. LOCAL void amiga_init()
  383.     {
  384.     oldpri = SetTaskPri(FindTask(0), 15);
  385.         /* make cursor invisible, speed up output some */
  386.     printf("\233""0 p\n");
  387.     obtain_audio();
  388.     create_queue();
  389.     allocate_channels();
  390.     init_timer();
  391.         /* audio filter OFF */
  392.     ciaa.ciapra |= CIAF_LED;
  393.     init_ui();
  394.     }
  395.  
  396. LOCAL void amiga_cleanup()
  397.     {
  398.     struct MinNode *current, *next;
  399.     
  400.     SetTaskPri(FindTask(0), oldpri);
  401.     printf("\233"" p\n");
  402.     if (win)
  403.         CloseWindow(win);
  404.     if (glist)
  405.         FreeGadgets(glist);
  406.     if (vi)
  407.         FreeVisualInfo(vi);
  408.     if (pub)
  409.         UnlockPubScreen(NULL, pub);
  410.     if (IntuitionBase)
  411.         CloseLibrary((struct Library *)IntuitionBase);
  412.     if (GadtoolsBase)
  413.         CloseLibrary(GadtoolsBase);
  414.     SCANLIST(current, next, &tracked_list, struct MinNode *)
  415.         FreeVec(current);
  416.    if (req->request.ioa_AllocKey)
  417.         free_channels();
  418.     if (audio_opened)
  419.         CloseDevice((struct IORequest *)req);
  420.     if (req)
  421.         DeleteIORequest(req);
  422.     if (port)
  423.         DeletePort(port);
  424.     while(first)
  425.         {
  426.         struct ext_audio *temp;
  427.         
  428.         temp = first;
  429.         first = first->next;
  430.         FreeMem(temp, sizeof(struct ext_audio));
  431.         }
  432.  
  433.     if (timer_opened)
  434.         {
  435.         if (!CheckIO((struct IORequest *)tr))
  436.             {
  437.             AbortIO((struct IORequest *)tr);
  438.             WaitIO((struct IORequest *)tr);
  439.             }
  440.         CloseDevice((struct IORequest *)tr);
  441.         }
  442.     if (tr)
  443.         DeleteIORequest(tr);
  444.     if (tport)
  445.         DeleteMsgPort(tport);
  446.     ciaa.ciapra &= ~CIAF_LED;
  447.     }
  448.  
  449. /* indispensible for not losing memory ! */
  450.  
  451. void __regargs __chkabort()
  452.     {
  453.     }
  454.     
  455. void *alloc_sample(int len)
  456.     {
  457.     char *s;
  458.     
  459.     s = AllocVec(len + sizeof(struct MinNode), MEMF_CHIP | MEMF_CLEAR);
  460.     if (!s)
  461.         return 0;
  462.     AddTail((struct List *)&tracked_list, (struct Node *)s);
  463.     return s + sizeof(struct MinNode);
  464.     }
  465.  
  466. void free_sample(char *s)
  467.     {
  468.     s -= sizeof(struct MinNode);
  469.     Remove((struct Node *)s);
  470.     FreeVec(s);
  471.     }
  472.  
  473. /* termio */
  474. boolean run_in_fg()
  475.     {
  476.     return 1;
  477.     }
  478.  
  479. void nonblocking_io()
  480.     {
  481.     static boolean yet = 0;
  482.     if (!yet)
  483.         {
  484.         NewList((struct List *)&tracked_list);
  485.         atexit(amiga_cleanup);
  486.         amiga_init();
  487.         yet = 1;
  488.         }
  489.     }
  490.  
  491. void sane_tty()
  492.     {
  493.     }
  494.     
  495. int may_getchar()
  496.     {
  497.     struct IntuiMessage *msg;
  498.     int id;
  499.     if (SetSignal(0,0) & SIGBREAKF_CTRL_C)
  500.         exit(10);
  501.     while(msg = GT_GetIMsg(win->UserPort))
  502.         switch(msg->Class)
  503.             {
  504.         case IDCMP_CLOSEWINDOW:
  505.             GT_ReplyIMsg(msg);
  506.             return 'q';
  507.         case IDCMP_REFRESHWINDOW:
  508.             GT_ReplyIMsg(msg);
  509.             GT_BeginRefresh(win);
  510.             GT_EndRefresh(win, TRUE);
  511.             break;
  512.         case IDCMP_GADGETUP:
  513.             id = ((struct Gadget *)msg->IAddress)->GadgetID;
  514.             GT_ReplyIMsg(msg);
  515.             return mapto[id];
  516.         default:
  517.             GT_ReplyIMsg(msg);
  518.             }
  519.     return -1;
  520.     }
  521.  
  522. /* audio */
  523.  
  524. /* empty operation on the amiga */
  525. void init_tables(oversample, frequency, chan)
  526. int oversample, frequency;
  527. struct channel *chan;
  528.     {
  529.     }
  530.  
  531. void resample(oversample, number)
  532. int oversample;
  533. int number;
  534.     {
  535.     int i;
  536. /*
  537.     if (first_time)
  538.         {
  539.         first_time = FALSE;
  540.         system_time.tv_micro += number;
  541.         }
  542. */
  543.         tr->tr_node.io_Command = TR_ADDREQUEST;
  544.         tr->tr_time.tv_secs = system_time.ev_hi;
  545.         tr->tr_time.tv_micro = system_time.ev_lo;
  546.         SendIO((struct IORequest *)tr);
  547.         WaitPort(tport);
  548.         while(GetMsg(tport))
  549.             ;
  550.     system_time.ev_lo += (unsigned long)number;
  551.     if (system_time.ev_lo < (unsigned long)number)
  552.         system_time.ev_hi++;
  553.     }
  554.  
  555. void play_note(au, samp, pitch)
  556. struct audio_channel *au;
  557. struct sample_info *samp;
  558. int pitch;
  559.     {
  560.     au->pitch = pitch;
  561.     send_immediate(CMD_FLUSH, 1 << au->amiga_number);
  562.     get_requests();
  563.     if (samp)
  564.         {
  565.         au->samp = samp;
  566.         if (au->samp->start)
  567.             {
  568.             first->request.ioa_Request.io_Command = CMD_WRITE;
  569.             first->request.ioa_Request.io_Flags = ADIOF_PERVOL;
  570.             first->request.ioa_Request.io_Unit = 1<<au->amiga_number;
  571.             first->request.ioa_Data = au->samp->start;
  572.             first->request.ioa_Length = au->samp->length;
  573.             first->request.ioa_Period = au->pitch;
  574.             first->request.ioa_Volume = au->volume;
  575.             first->request.ioa_Cycles = 1;
  576.             BeginIO((struct IORequest *)first);
  577.             first = first->next;
  578.             if (au->samp->rp_start)
  579.                 {
  580.                 first->request.ioa_Request.io_Command = CMD_WRITE;
  581.                 first->request.ioa_Request.io_Flags = 0;
  582.                 first->request.ioa_Request.io_Unit = 1<<au->amiga_number;
  583.                 first->request.ioa_Data = au->samp->rp_start;
  584.                 first->request.ioa_Length = au->samp->rp_length;
  585.                 first->request.ioa_Cycles = 0;
  586.                 BeginIO((struct IORequest *)first);
  587.                 first = first->next;
  588.                 }  
  589.             }
  590.         }
  591.     }
  592.  
  593. void set_play_pitch(au, pitch)
  594. struct audio_channel *au;
  595. int pitch;
  596.     {
  597.     if (pitch != au->pitch)
  598.         {
  599.         au->pitch = pitch;
  600.         first->request.ioa_Period = pitch;
  601.         first->request.ioa_Volume = au->volume;
  602.         send_immediate(ADCMD_PERVOL, 1 << au->amiga_number);
  603.         }
  604.     }
  605.  
  606. void set_play_volume(au, volume)
  607. struct audio_channel *au;
  608. int volume;
  609.     {
  610.     if (volume != au->volume)
  611.         {
  612.         au->volume = volume;
  613.         first->request.ioa_Period = au->pitch;
  614.         first->request.ioa_Volume = volume;
  615.         send_immediate(ADCMD_PERVOL, 1 << au->amiga_number);
  616.         }
  617.     }
  618.  
  619. void set_play_position(au, pos)
  620. struct audio_channel *au;
  621. int pos;
  622.     {
  623.     send_immediate(CMD_FLUSH, 1 << au->amiga_number);
  624.     get_requests();
  625.     if (au->samp->start)
  626.         {
  627.         if (pos < au->samp->length)
  628.             {
  629.             first->request.ioa_Request.io_Command = CMD_WRITE;
  630.             first->request.ioa_Request.io_Flags = ADIOF_PERVOL;
  631.             first->request.ioa_Request.io_Unit = 1<<au->amiga_number;
  632.             first->request.ioa_Data = au->samp->start + pos;
  633.             first->request.ioa_Length = au->samp->length - pos;
  634.             first->request.ioa_Period = au->pitch;
  635.             first->request.ioa_Volume = au->volume;
  636.             first->request.ioa_Cycles = 1;
  637.             BeginIO((struct IORequest *)first);
  638.             first = first->next;
  639.             if (au->samp->rp_start)
  640.                 {
  641.                 first->request.ioa_Request.io_Command = CMD_WRITE;
  642.                 first->request.ioa_Request.io_Flags = 0;
  643.                 first->request.ioa_Request.io_Unit = 1<<au->amiga_number;
  644.                 first->request.ioa_Data = au->samp->rp_start;
  645.                 first->request.ioa_Length = au->samp->rp_length;
  646.                 first->request.ioa_Cycles = 0;
  647.                 BeginIO((struct IORequest *)first);
  648.                 first = first->next;
  649.                 }  
  650.             }
  651.         }
  652.     }
  653.  
  654.  
  655. void set_mix(percent)
  656. int percent;
  657.     {
  658.     }
  659.  
  660. int open_audio(f, s)
  661. int f, s;
  662.     {
  663.     first_time = TRUE;
  664.     return ReadEClock(&system_time);        /* samples/sec used as a timing unit: 1sec =1 000 000 µs */
  665.     }
  666.     
  667. void set_synchro(s)
  668. boolean s;
  669.     {
  670.     ReadEClock(&system_time);
  671.     first_time = TRUE;
  672.     }
  673.  
  674. int update_frequency()
  675.     {
  676.     return 0;
  677.     }
  678.  
  679. void output_samples(int left, int right)
  680.     {
  681.     }
  682.     
  683. void flush_buffer(void)
  684.     {
  685.     }
  686.  
  687. void discard_buffer(void)
  688.     {
  689.     reset_audio();
  690.     }
  691.     
  692. void close_audio(void)
  693.     {
  694.     }
  695.     
  696.  
  697.     
  698. /* amiga: popen() for OS2_04 */
  699.  
  700.  
  701. #include <proto/dos.h>
  702. #include <dos/dostags.h>
  703. #include <stdio.h>
  704. #include <string.h>
  705.  
  706. /*
  707. ###    CSupport/popen
  708. ###
  709. ###    NAME
  710. ###        popen/pclose -- Unix-like pipes
  711. ###
  712. ###    STATUS
  713. ###        Experimental
  714. ###
  715.  */
  716. FILE *popen(char *command, char *mode)
  717.     {
  718.     
  719.     if (strcmp(mode, "r") == 0)
  720.         /* open pipe for reading */
  721.         {
  722.         FILE *reader;
  723.         BPTR writer, null;
  724.  
  725.         writer = Open("PIPE:", MODE_NEWFILE);
  726.         reader = fopen("PIPE:", "r");
  727.         null = Open("NIL:", MODE_NEWFILE);
  728.         if (SystemTags(command, SYS_Input, null, 
  729.             SYS_Output, writer, SYS_Asynch, TRUE, TAG_END) == -1)
  730.             {
  731.             Close(null);
  732.             Close(writer);
  733.             fclose(reader);
  734.             return NULL;
  735.             }
  736.         else
  737.             return reader;
  738.         }
  739.     else if (strcmp(mode, "w") == 0)
  740.         /* open pipe for writing */
  741.         {
  742.         FILE *writer;
  743.         BPTR reader, null;
  744.         
  745.         writer = fopen("PIPE:", "w");
  746.         reader = Open("PIPE:", MODE_OLDFILE);
  747.         null = Open("NIL:", MODE_NEWFILE);
  748.         if (SystemTags(command, SYS_Input, reader, 
  749.             SYS_Output, null, SYS_Asynch, TRUE, TAG_END) == -1)
  750.             {
  751.             Close(null);
  752.             Close(reader);
  753.             fclose(writer);
  754.             return NULL;
  755.             }
  756.         else
  757.             return writer;
  758.         }
  759.     else
  760.         return NULL;
  761.     }
  762.  
  763. /* for us, pclose is just fclose.
  764.  * We hope everything will close out alright
  765.  */
  766. void pclose(FILE *f)
  767.     {
  768.     fclose(f);
  769.     }
  770.  
  771.